/******************************************************************************
 cnome: A C++ source "genes" discover. :-)

 See more details at:

   http://www.codeplex.com/cnome

 The original author site is at: http://www.alexandre-barbosa.com/

 Copyright (c) 2008 Alexandre Barbosa.
 Distributed under the MIT License. (See accompanying file LICENSE-MIT.txt 
 or at http://www.codeplex.com/cnome/license)
 ******************************************************************************/

#if defined(_MSC_VER) && (_MSC_VER >= 1400)
#pragma warning ( disable : 4996 )
#pragma warning ( disable : 4800 )
#pragma warning ( disable : 4355 )
#pragma warning ( disable : 4267 )
#endif

#include <boost/wave/cpplexer/cpp_lex_iterator.hpp> // lexer class

#include <iostream>
#include <list>
#include <map>
#include <stdio.h>
#include <string>


#include "cnome_iterator.h"
#include "cnome_typedefs.h"
#include "scopes.h"
#include "symbol_item.h"
#include "tst_avl.h"
#include "util.h"

///////////////////////////////////////////////////////////////////////////////
using namespace std;
using namespace boost::spirit;
using namespace boost::wave;
using namespace cnome::scopes;
using namespace cnome::util;

namespace cnome {
const iterator_T cnome_iterator::end;
static cnome_iterator stt_lcl_farest;
cnome_iterator& cnome_iterator::m_farest = stt_lcl_farest;

cnome_iterator::cnome_iterator():iterator_T(), m_pos(0) {}

cnome_iterator::cnome_iterator(const cnome_iterator& a): 
    iterator_T(static_cast<context_t::iterator_type>(a)), 
    m_pos(a.m_pos) {
}

cnome_iterator::cnome_iterator(const context_t::iterator_type& a):
    iterator_T(a),
    m_pos(0) {
    if (    *this != end 
         && (    (**this == T_PP_LINE) || (**this == T_SPACE) || (**this == T_NEWLINE) 
              || (**this == T_CPPCOMMENT) || (**this == T_PP_PRAGMA) || (**this == T_MSEXT_CDECL)
              || (**this == T_EOF) || (**this == T_MSEXT_ASM) || (**this == T_CCOMMENT) ) // EOF is sent for MACRO= (empty)
       )
        ++(*this);
}

const cnome_iterator& cnome_iterator::operator++() {
        const char* pc = (*this != end)?this->iterator_T::operator*().get_value().c_str():0;
        if (*this != end && (**this == T_PP_LINE || **this == T_CPPCOMMENT) || (**this == T_PP_PRAGMA))
            while (*this != end && **this != T_NEWLINE) {
                this->iterator_T::operator++();
                pc = (*this != end)?this->iterator_T::operator*().get_value().c_str():0;
            }

    this->iterator_T::operator++();
    pc = (*this != end)?this->iterator_T::operator*().get_value().c_str():0;

    while (    (*this != end) 
            && (    (**this == T_PP_LINE) || (**this == T_SPACE) || (**this == T_NEWLINE) 
                 || (**this == T_CPPCOMMENT) || (**this == T_PP_PRAGMA) || (**this == T_MSEXT_CDECL)
                 || (**this == T_EOF) || (**this == T_CCOMMENT)
               ) 
          ) {
        if (*this != end &&  (**this == T_PP_LINE || **this == T_CPPCOMMENT || **this == T_PP_PRAGMA))
            while (*this != end && **this != T_NEWLINE) {
                this->iterator_T::operator++();
                pc = (*this != end)?this->iterator_T::operator*().get_value().c_str():0;
            }
        while ((*this != end) && (    (**this == T_SPACE) || (**this == T_NEWLINE) || (**this == T_CPPCOMMENT) 
                                   || (**this == T_MSEXT_CDECL) || (**this == T_EOF) || (**this == T_CCOMMENT))) {
            this->iterator_T::operator++();
            pc = (*this != end)?this->iterator_T::operator*().get_value().c_str():0;
        }

    }

    // Handle weird case of
    // #    define    _WPAD    /##/
    // _WPAD a;
    while (       (*this != end)
            && (**this == T_DIVIDE && this->iterator_T::operator*().get_value() == "//" ) ) {
        while (*this != end && **this != T_NEWLINE) {
            this->iterator_T::operator++();
            pc = (*this != end)?this->iterator_T::operator*().get_value().c_str():0;
        }
        while (*this != end && **this == T_NEWLINE) {
            this->iterator_T::operator++();
            pc = (*this != end)?this->iterator_T::operator*().get_value().c_str():0;
        }
    }

    while (*this != end && **this == T_MSEXT_ASM) {
        handleMSASM();
        pc = (*this != end)?this->iterator_T::operator*().get_value().c_str():0;
    }

#if defined(CNOME_DEBUG_)
	if (**this == T_IDENTIFIER) {  // Search symbol table
        std::string curScope;
        get_scope(curScope, (*this)->get_value().c_str());
        scopesFromStr scopes(curScope);
        while (scopes.nextScope().size()) {
            if (symbolItem* it = find(cnome_symbols, scopes.current_scope().c_str())) {
                (*this)->set_token_id(*it);
                break;
            }    
        }
    }
#endif

    m_pos++;
    if (m_pos > m_farest.m_pos)
        m_farest = *this;

    return *this;
}

const cnome_iterator cnome_iterator::operator++(int) {
    token_t tk = **this;

    cnome_iterator clone = *this;
    operator++();

#if defined(CNOME_DEBUG_)
	if (*clone == T_IDENTIFIER) {  // Search symbol table
        std::string curScope;
        get_scope(curScope, clone->get_value().c_str());
        scopesFromStr scopes(curScope);
        while (scopes.nextScope().size()) {
            if (symbolItem* it = find(cnome_symbols, scopes.current_scope().c_str())) {
                clone->set_token_id(*it);
                break;
            }    
        }
    }
#endif

    return clone;
}

token_t& cnome_iterator::operator*() {
	/*
    if (this->iterator_T::operator*() == T_IDENTIFIER) {  // Search symbol table
        std::string curScope;
        get_scope(curScope, this->iterator_T::operator*().get_value().c_str());
        scopesFromStr scopes(curScope, this->iterator_T::operator*().get_value().size());
        while (scopes.nextScope().size()) {
            itSymbolItem it;
            if ((it = cnome_symbols.find(scopes.current_scope().c_str())) != cnome_symbols.end()) {
                if (*(it->second)!=T_IDENTIFIER || it->second->get_identifier_type() != symbolItem::T_NAMESPACE_ALIAS)
                    this->iterator_T::operator*().set_token_id(*(it->second));
                else {
                    this->iterator_T::operator*().set_token_id(*(it->second->get_type_symbol()));
                    this->iterator_T::operator*().set_value(it->second->get_type_symbol()->get_value());
                }

                break;
            }    
        }
    }*/

    if (   this->iterator_T::operator*() == T_CLASS 
        && this->iterator_T::operator*().get_value() != "class") // Handle the weird case of preprocessor "class##n"
        this->iterator_T::operator*().set_token_id(T_IDENTIFIER);

    return this->iterator_T::operator*();
}

void cnome_iterator::handleMSASM() {
    token_t aux = this->iterator_T::operator*();
    if (!(*this != end && **this == T_MSEXT_ASM))    
        return; // Nothing to do

    // Get next meaningfull token
    do {
       this->iterator_T::operator++();
       aux = this->iterator_T::operator*();
    }
    while (*this != end && (**this == T_CCOMMENT || **this == T_CPPCOMMENT) || (**this == T_NEWLINE) || (**this == T_SPACE)); // TODO: Verify valid tokens here
    token_id endTk;
    for (endTk = (**this==T_LEFTBRACE)?T_RIGHTBRACE:T_NEWLINE; *this != end && **this != endTk && **this != T_RIGHTBRACE; // Skip end token
             this->iterator_T::operator++())
         aux = this->iterator_T::operator*();
    if (*this != end && **this == endTk) {
       this->iterator_T::operator++();
       aux = this->iterator_T::operator*();
    }
    while (    (*this != end) 
            && (    (**this == T_PP_LINE) || (**this == T_SPACE) || (**this == T_NEWLINE) 
                 || (**this == T_CPPCOMMENT) || (**this == T_PP_PRAGMA) || (**this == T_MSEXT_CDECL)
                 || (**this == T_EOF) || (**this == T_CCOMMENT)
               ) 
          ) {
        if (*this != end &&  (**this == T_PP_LINE || **this == T_CPPCOMMENT || **this == T_PP_PRAGMA))
            while (*this != end && **this != T_NEWLINE)
                this->iterator_T::operator++();
        while ((*this != end) && (    (**this == T_SPACE) || (**this == T_NEWLINE) || (**this == T_CPPCOMMENT) 
                                   || (**this == T_MSEXT_CDECL) || (**this == T_EOF) || (**this == T_CCOMMENT)))
            this->iterator_T::operator++();
    }
}

bool operator<(const cnome_iterator& a, const cnome_iterator& b) {
    return a.m_pos < b.m_pos;
}

bool operator==(const cnome_iterator& a, const cnome_iterator& b) {
    bool a_isEof = static_cast<const iterator_T>(a) == a.end;
    bool b_isEof = static_cast<const iterator_T>(b) == b.end;

    if (a_isEof && b_isEof)
        return true;  // Both are eof
    if (a_isEof ^ b_isEof)
        return false; // Only one is eof

    return a.m_pos == b.m_pos;
}
} //  namespace cnome

#if defined(_MSC_VER) && (_MSC_VER >= 1400)
#pragma warning ( default : 4996 )
#pragma warning ( default : 4800 )
#pragma warning ( default : 4355 )
#pragma warning ( default : 4267 )
#endif
